package com.anlu.base.jdk.jdk8;

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 函数式编程工具类
 * 提供常用的函数式编程操作的便捷方法
 */
public class FunctionalUtils {

    /**
     * 将一个值应用到多个函数上，并返回结果列表
     *
     * @param value 要应用的值
     * @param functions 函数数组
     * @param <T> 输入值类型
     * @return 函数应用结果列表（元素类型为Object，可接受任何类型）
     */
    @SafeVarargs
    public static <T> List<Object> applyFunctions(T value, Function<T, ?>... functions) {
        return Arrays.stream(functions)
                .map(func -> func.apply(value))
                .collect(Collectors.toList());
    }

    /**
     * 组合多个函数，从左到右依次应用
     *
     * @param functions 要组合的函数
     * @param <T> 输入和输出类型
     * @return 组合后的函数
     */
    @SafeVarargs
    public static <T> Function<T, T> composeLeftToRight(Function<T, T>... functions) {
        return Arrays.stream(functions)
                .reduce(Function.identity(), Function::andThen);
    }

    /**
     * 组合多个函数，从右到左依次应用（数学意义上的函数组合）
     *
     * @param functions 要组合的函数
     * @param <T> 输入和输出类型
     * @return 组合后的函数
     */
    @SafeVarargs
    public static <T> Function<T, T> composeRightToLeft(Function<T, T>... functions) {
        return Arrays.stream(functions)
                .reduce(Function.identity(), Function::compose);
    }

    /**
     * 创建一个可以缓存结果的函数（记忆化）
     *
     * @param function 原始函数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有缓存功能的函数
     */
    public static <T, R> Function<T, R> memoize(Function<T, R> function) {
        Map<T, R> cache = new HashMap<>();
        return t -> cache.computeIfAbsent(t, function);
    }

    /**
     * 创建一个可以重试的函数
     *
     * @param function 原始函数
     * @param retries 重试次数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有重试功能的函数
     */
    public static <T, R> Function<T, R> withRetry(Function<T, R> function, int retries) {
        return t -> {
            Exception lastException = null;
            for (int i = 0; i <= retries; i++) {
                try {
                    return function.apply(t);
                } catch (Exception e) {
                    lastException = e;
                    if (i < retries) {
                        try {
                            Thread.sleep(100 * (i + 1)); // 简单的退避策略
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            throw new RuntimeException(ie);
                        }
                    }
                }
            }
            throw new RuntimeException("Function failed after " + retries + " retries", lastException);
        };
    }

    /**
     * 创建一个带超时的函数
     *
     * @param function 原始函数
     * @param timeoutMillis 超时时间（毫秒）
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有超时功能的函数
     */
    public static <T, R> Function<T, R> withTimeout(Function<T, R> function, long timeoutMillis) {
        return t -> {
            final Object lock = new Object();
            final Object[] result = new Object[1];
            final Exception[] exception = new Exception[1];

            Thread thread = new Thread(() -> {
                try {
                    R r = function.apply(t);
                    synchronized (lock) {
                        result[0] = r;
                        lock.notify();
                    }
                } catch (Exception e) {
                    synchronized (lock) {
                        exception[0] = e;
                        lock.notify();
                    }
                }
            });

            thread.start();

            synchronized (lock) {
                try {
                    lock.wait(timeoutMillis);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(e);
                }
            }

            if (exception[0] != null) {
                throw new RuntimeException(exception[0]);
            }

            if (result[0] == null && thread.isAlive()) {
                thread.interrupt();
                throw new RuntimeException("Function timed out after " + timeoutMillis + " milliseconds");
            }

            return (R) result[0];
        };
    }

    /**
     * 创建一个可以记录日志的函数
     *
     * @param function 原始函数
     * @param logger 日志记录函数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有日志记录功能的函数
     */
    public static <T, R> Function<T, R> withLogging(Function<T, R> function, BiConsumer<T, R> logger) {
        return t -> {
            R result = function.apply(t);
            logger.accept(t, result);
            return result;
        };
    }

    /**
     * 将Consumer转换为Function
     *
     * @param consumer 消费者函数
     * @param <T> 输入类型
     * @return 转换后的函数
     */
    public static <T> Function<T, Void> fromConsumer(Consumer<T> consumer) {
        return t -> {
            consumer.accept(t);
            return null;
        };
    }

    /**
     * 将Supplier转换为Function
     *
     * @param supplier 供应函数
     * @param <R> 输出类型
     * @return 转换后的函数
     */
    public static <R> Function<Object, R> fromSupplier(Supplier<R> supplier) {
        return ignored -> supplier.get();
    }

    /**
     * 创建一个可以处理异常的函数
     *
     * @param function 原始函数
     * @param exceptionHandler 异常处理函数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有异常处理功能的函数
     */
    public static <T, R> Function<T, R> withExceptionHandling(
            Function<T, R> function, 
            BiFunction<T, Exception, R> exceptionHandler) {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception e) {
                return exceptionHandler.apply(t, e);
            }
        };
    }

    /**
     * 创建一个可以部分应用的函数（柯里化辅助）
     *
     * @param biFunction 二元函数
     * @param firstArgument 第一个参数
     * @param <T> 第一个参数类型
     * @param <U> 第二个参数类型
     * @param <R> 返回类型
     * @return 部分应用后的函数
     */
    public static <T, U, R> Function<U, R> partial(BiFunction<T, U, R> biFunction, T firstArgument) {
        return secondArgument -> biFunction.apply(firstArgument, secondArgument);
    }

    /**
     * 创建一个可以链式调用的函数组合器
     *
     * @param functions 函数列表
     * @param <T> 输入输出类型
     * @return 链式函数
     */
    @SafeVarargs
    public static <T> Function<T, T> chain(Function<T, T>... functions) {
        return Arrays.stream(functions)
                .reduce(Function.identity(), Function::andThen);
    }

    /**
     * 创建一个可以条件执行的函数
     *
     * @param condition 条件判断函数
     * @param trueFunction 条件为真时执行的函数
     * @param falseFunction 条件为假时执行的函数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 条件执行函数
     */
    public static <T, R> Function<T, R> conditional(
            Predicate<T> condition,
            Function<T, R> trueFunction,
            Function<T, R> falseFunction) {
        return t -> condition.test(t) ? trueFunction.apply(t) : falseFunction.apply(t);
    }

    /**
     * 创建一个可以收集操作历史的函数
     *
     * @param function 原始函数
     * @param history 历史记录列表
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 带有历史记录功能的函数
     */
    public static <T, R> Function<T, R> withHistory(Function<T, R> function, List<String> history) {
        return t -> {
            long startTime = System.nanoTime();
            R result = function.apply(t);
            long endTime = System.nanoTime();
            history.add(String.format("Input: %s, Output: %s, Time: %d ns", t, result, endTime - startTime));
            return result;
        };
    }

    /**
     * 创建一个可以限制执行次数的函数
     *
     * @param function 原始函数
     * @param maxCalls 最大调用次数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 限制执行次数的函数
     */
    public static <T, R> Function<T, R> withCallLimit(Function<T, R> function, int maxCalls) {
        int[] callCount = {0};
        return t -> {
            if (callCount[0] >= maxCalls) {
                throw new RuntimeException("Function call limit exceeded: " + maxCalls);
            }
            callCount[0]++;
            return function.apply(t);
        };
    }
}